﻿Option Explicit On
Option Strict On

Imports System
Imports System.IO.Ports

Namespace RS232

    ''' <summary>
    ''' Provides access to an RS232 Port
    ''' </summary>
    ''' <remarks></remarks>
    Public Class Port
        Implements IDisposable

#Region " Private Members "

        Private WithEvents m_Port As New IO.Ports.SerialPort

#End Region

#Region " Events "

        Public Event DataReceived As EventHandler(Of DataReceivedEventArgs)
        Public Event PortClosed As EventHandler(Of EventArgs)

#End Region

#Region " Shared Methods "

        ''' <summary>
        ''' Returns a list of available Com port names
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Shared Function GetPortNames() As String()
            Return SerialPort.GetPortNames
        End Function

        ''' <summary>
        ''' Forces a com port closed
        ''' </summary>
        ''' <param name="PortName"></param>
        ''' <remarks></remarks>
        Public Shared Sub ForcePortClosed(ByVal PortName As String)
            Using Port As New SerialPort(PortName)
                Port.Close()
            End Using
        End Sub

#End Region

#Region " Public Properties "

        Public Property ReceivedBytesThreshold() As Integer
            Get
                Return m_Port.ReceivedBytesThreshold
            End Get
            Set(ByVal value As Integer)
                m_Port.ReceivedBytesThreshold = value
            End Set
        End Property

        Public ReadOnly Property IsOpen() As Boolean
            Get
                Return m_Port.IsOpen
            End Get
        End Property

        Public Property RtsEnable As Boolean
            Get
                Return m_Port.RtsEnable
            End Get
            Set(ByVal value As Boolean)
                m_Port.RtsEnable = value
            End Set
        End Property

        Public Property DtrEnable As Boolean
            Get
                Return m_Port.DtrEnable
            End Get
            Set(ByVal value As Boolean)
                m_Port.DtrEnable = value
            End Set
        End Property
#End Region

#Region " Public Methods "

        ''' <summary>
        ''' 
        ''' </summary>
        ''' <param name="Parameters"></param>
        ''' <remarks></remarks>
        Public Sub Connect(ByVal Parameters As PortParameters)

            If Parameters Is Nothing Then
                Throw New ArgumentNullException("Parameters")
            End If

            m_Port.BaudRate = Parameters.BaudRate
            m_Port.PortName = Parameters.PortName
            m_Port.DtrEnable = Parameters.DtrEnable
            m_Port.RtsEnable = Parameters.RtsEnable
            m_Port.StopBits = Parameters.StopBits
            m_Port.DataBits = Parameters.DataBits
            m_Port.Handshake = Parameters.Handshake
            m_Port.Parity = Parameters.Parity
            m_Port.WriteTimeout = 2000

            'Run the Port Fixer to avoid some odd .NET Framework Issues with it's SerialPort implementation
            'PortFixer.Execute(Parameters.PortName)

            Try
                m_Port.Open()
            Catch ex As Exception
                m_Port.Close()
                RaiseEvent PortClosed(Me, New EventArgs)
            End Try

        End Sub

        Public Sub Disconnect()

            If m_Port.IsOpen Then
                m_Port.DiscardInBuffer()
                m_Port.DiscardOutBuffer()
                m_Port.Close()
            End If

        End Sub

        Public Sub SendData(ByVal Data() As Char)
            If Data Is Nothing Then
                Throw New ArgumentNullException("Data")
            End If

            If m_Port.IsOpen Then
                m_Port.Write(Data, 0, Data.Length)
            End If
        End Sub

        Public Sub SendData(ByVal Data() As Byte)
            If Data Is Nothing Then
                Throw New ArgumentNullException("Data")
            End If

            If m_Port.IsOpen Then
                m_Port.Write(Data, 0, Data.Length)
            End If
        End Sub

        Public Sub SendData(ByVal Data As String)
            If m_Port.IsOpen Then
                Try
                    m_Port.Write(Data)
                Catch ext As TimeoutException
                    Throw New Exception("Timeout writing to Com port " & m_Port.PortName, ext)
                    m_Port.Close()
                Catch ex As Exception
                    Throw New Exception("Error writing to Com port " & m_Port.PortName & ":" & ex.Message, ex)
                End Try
            End If
        End Sub

        Public Sub FlushBuffer()
            m_Port.DiscardInBuffer()
        End Sub

#End Region

#Region " Dispose Code "

        Private disposedValue As Boolean ' To detect redundant calls

        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    m_Port.Dispose()
                End If
            End If
            Me.disposedValue = True
        End Sub

#Region " IDisposable Support "
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
#End Region

        Private Sub m_Port_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles m_Port.DataReceived

            Dim ReceivedData(m_Port.BytesToRead - 1) As Byte
            m_Port.Read(ReceivedData, 0, ReceivedData.Length)

            'Generate the String Data
            Dim StringData As String
            Dim ASCIIEncoder As New System.Text.ASCIIEncoding()
            StringData = ASCIIEncoder.GetString(ReceivedData)

            'Dim Data As String = m_Port.ReadExisting
            RaiseEvent DataReceived(Me, New DataReceivedEventArgs(StringData, CType(ReceivedData.Clone(), Byte())))

        End Sub

#End Region

        Private Sub m_Port_ErrorReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialErrorReceivedEventArgs) Handles m_Port.ErrorReceived
            Throw New Exception("Error Received from COM port " & m_Port.PortName)
        End Sub

    End Class

End Namespace